<?php
declare(strict_types=1);

namespace App\Utils\Common\NeopayRsa {
    use App\Exception\InternalException;
    use function Hyperf\Translation\__;
    use function App\Utils\Common\Aes\encrypt as aesEncrypt;

    /**
     * 生成随机字符串
     * @param int $size
     * @return string
     * @throws \Exception
     */
    function generate(int $size, bool $isInt=true): string
    {
        $str = '0123456789';
        if ($isInt === false) {
            $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        }
        $data = [];
        if (function_exists('random_int')) {
            for ($i = 0; $i < $size; $i++) {
                $data[] = $str[random_int(0, strlen($str) - 1)];
            }
        } else {
            for ($i = 0; $i < $size; $i++) {
                mt_srand(crc32(uniqid(). microtime()));
                $data[] = $str[mt_rand(0, strlen($str) - 1)];
            }
        }

        return implode('', $data);
    }

    /**
     * 加密数据
     * @param string $data
     * @param string $publicCertPath
     * @return string
     */
    function encrypt(string $data, string $publicCertPath): string
    {
        $publicKey = loadPublicKey($publicCertPath);

        $dataArr = str_split($data, 117);

        $encryptedData = [];
        foreach ($dataArr as $item) {
            $encryptedItem = '';
            // 加密数据
            if (!openssl_public_encrypt($item, $encryptedItem, $publicKey)) {
                throw new InternalException(__('message.openssl.encrypt_failed', [
                    'message' => openssl_error_string()
                ]));
            }
            $encryptedData[] = $encryptedItem;
        }

        return implode('', $encryptedData);
    }

    /**
     * 解密数据
     * @param string $encryptData
     * @param string $privateCertPath
     * @return string
     */
    function decrypt(string $encryptData, string $privateCertPath): string
    {
        $privateKey = loadPrivateKey($privateCertPath);

        $decryptedData = [];
        $dataArray = str_split($encryptData, 128);

        foreach ($dataArray as $item) {
            $decryptedItem = '';
            // 解密数据
            if (!openssl_private_decrypt($item, $decryptedItem, $privateKey)) {
                throw new InternalException(__('message.openssl.decrypt_failed', [
                    'message' => openssl_error_string()
                ]));
            }
            $decryptedData[] = $decryptedItem;
        }

        return implode('', $decryptedData);
    }

    /**
     * 私钥加签
     * @param string $data
     * @param string $privateCertPath
     * @return string
     */
    function sign(string $data, string $privateCertPath): string
    {
        $privateKey = loadPrivateKey($privateCertPath);

        $dataArray = str_split($data, 117);
        $encryptedData = [];

        foreach ($dataArray as $item) {
            // 加密数据
            if (!openssl_private_encrypt($item, $encryptedItem, $privateKey)) {
                throw new InternalException(__('message.openssl.encrypt_failed', [
                    'message' => openssl_error_string()
                ]));
            }
            $encryptedData[] = $encryptedItem;
        }

        return implode('', $encryptedData);
    }

    /**
     * 验证签名
     * @param $encryptData
     * @return string
     */
    function verify(string $encryptData, string $publicCertPath): string
    {
        $publicKey = loadPubKey($publicCertPath);

        $decrypted = [];
        $dataArray = str_split($encryptData, 128);

        foreach ($dataArray as $item) {
            $decryptedItem = '';
            $result = openssl_public_decrypt($item, $decryptedItem, $publicKey);
            if (!$result) {
                throw new InternalException(__('message.openssl.decrypt_failed', [
                    'message' => openssl_error_string()
                ]));
            }

            $decrypted[] = $decryptedItem;
        }

        return implode('', $decrypted);
    }

    /**
     * 加载私钥
     * @param string $data
     * @param string $publicKey
     * @return array
     * @throws \Exception
     */
    function encryptByPublicKey(string $data, string $publicKey): array
    {
        $key = generate(16, false);
        // AES加密数据
        $encryptInfoStr = base64_encode(aesEncrypt($key, $data));
        // 公钥加密密钥
        $dgtlEnvlp = base64_encode(encrypt($key, $publicKey));

        return [
            'encryptInfo' => $encryptInfoStr,
            'dgtlEnvlp' => $dgtlEnvlp,
        ];
    }

    /**
     * 加载公钥
     * @param string $publicCertPath
     * @return \OpenSSLAsymmetricKey
     */
    function loadPublicKey(string $publicCertPath): \OpenSSLAsymmetricKey
    {
        if (!$publicCertPath) {
            throw new InternalException(__('message.config.public_cert'));
        }
        $pubKey = $publicCertPath;
        if (file_exists($publicCertPath)) {
            // 读取证书
            $pubKey = file_get_contents($publicCertPath);
        }

        $pubKey = preg_replace('/^\s+/', '', $pubKey);

        // 转换为openssl密钥
        $publicKey = openssl_pkey_get_public($pubKey);

        if (!$publicKey) {
            throw new InternalException(__('message.openssl.key_format', [
                'message' => openssl_error_string()
            ]));
        }

        return $publicKey;
    }

    /**
     * 加载公钥
     * @param string $publicCertPath
     * @return \OpenSSLAsymmetricKey
     */
    function loadPubKey(string $publicCertPath): \OpenSSLAsymmetricKey
    {
        if (!$publicCertPath) {
            throw new InternalException(__('message.config.public_cert'));
        }
        $pubKey = $publicCertPath;
        if (file_exists($publicCertPath)) {
            // 读取证书
            $pubKey = file_get_contents($publicCertPath);
        }

        $pubKey = preg_replace('/^\s+/', '', $pubKey);
        // 转换为openssl密钥
        $publicKey = openssl_get_publickey($pubKey);

        if (!$publicKey) {
            throw new InternalException(__('message.openssl.key_format', [
                'message' => openssl_error_string()
            ]));
        }

        return $publicKey;
    }

    /**
     * 加载私钥
     * @param string $privateCertPath
     * @return \OpenSSLAsymmetricKey
     */
    function loadPrivateKey(string $privateCertPath): \OpenSSLAsymmetricKey
    {
        if (!$privateCertPath || !file_exists($privateCertPath)) {
            throw new InternalException(__('message.config.private_cert'));
        }
        // 读取私钥
        $privateKey = file_get_contents($privateCertPath);

        // 转换为openssl密钥
        $privateKey = openssl_pkey_get_private($privateKey);

        if (!$privateKey) {
            throw new InternalException(__('message.openssl.key_format', [
                'message' => openssl_error_string()
            ]));
        }

        return $privateKey;
    }
}